<?php

namespace app\common\webdav\browser;

use Sabre\DAV\Browser\Plugin as BrowserPlugin;
use Sabre\DAV;
use Sabre\DAV\Browser\HtmlOutput;
use Sabre\DAV\Browser\HtmlOutputHelper;
use Sabre\DAV\Browser\PropFindAll;
use think\facade\View;
use Sabre\HTTP;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Sabre\Uri;

use Sabre\DAV\MkCol;


class Plugin extends BrowserPlugin
{

    /**
     * Generates the first block of HTML, including the <head> tag and page
     * header.
     *
     * Returns footer.
     *
     * @param string $title
     * @param string $path
     *
     * @return string
     */
    public function generateHeader($title, $path = null)
    {

        $vars = [
            'title' => ($title),
            'favicon' => '/favicon.ico',
            'style' => '/static/webdav/css/index.css',
            'iconstyle' => ($this->getAssetUrl('openiconic/open-iconic.css')),
            'logo' => '/static/common/images/logo-2.png',
            'baseUrl' => $this->server->getBaseUri(),
        ];

        View::assign('vars', $vars);

        $html = View::fetch(__DIR__ . '/assets/html/header.html');

        // If the path is empty, there's no parent.
        if ($path) {
            list($parentUri) = Uri\split($path);
            $fullPath = $this->server->getBaseUri() . HTTP\encodePath($parentUri);
            $html .= '<a href="' . $fullPath . '" class="btn">⇤ 上一层</a>';
        } else {
            $html .= '<span class="btn disabled">⇤ 根目录</span>';
        }

        $html .= ' <a href="/home" class="btn"></span>官网</a>';
        $html .= ' <a href="/home" class="btn"></span>前往网盘面板</a>';

        $html .= '</nav>';

        return $html;
    }
    /**
     * Generates the page footer.
     *
     * Returns html.
     *
     * @return string
     */
    public function generateFooter()
    {

        return View::fetch(__DIR__ . '/assets/html/footer.html');
    }

    /**
     * Generates the html directory index for a given url.
     *
     * @param string $path
     *
     * @return string
     */
    public function generateDirectoryIndex($path)
    {
        $html = $this->generateHeader($path ? $path : '/', $path);

        $node = $this->server->tree->getNodeForPath($path);
        if ($node instanceof DAV\ICollection) {
            $html .= "<section><h1>目录列表</h1>\n";
            $html .= '<table class="nodeTable">';

            $subNodes = $this->server->getPropertiesForChildren($path, [
                '{DAV:}displayname',
                '{DAV:}resourcetype',
                '{DAV:}getcontenttype',
                '{DAV:}getcontentlength',
                '{DAV:}getlastmodified',
            ]);

            foreach ($subNodes as $subPath => $subProps) {
                $subNode = $this->server->tree->getNodeForPath($subPath);
                $fullPath = $this->server->getBaseUri() . HTTP\encodePath($subPath);
                list(, $displayPath) = Uri\split($subPath);

                $subNodes[$subPath]['subNode'] = $subNode;
                $subNodes[$subPath]['fullPath'] = $fullPath;
                $subNodes[$subPath]['displayPath'] = $displayPath;
            }
            uasort($subNodes, [$this, 'compareNodes']);

            foreach ($subNodes as $subProps) {
                $type = [
                    'string' => 'Unknown',
                    'icon' => 'cog',
                ];
                if (isset($subProps['{DAV:}resourcetype'])) {
                    $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']);
                }

                $html .= '<tr>';
                $html .= '<td class="nameColumn"><a href="' . $this->escapeHTML($subProps['fullPath']) . '"><span class="oi" data-glyph="' . $this->escapeHTML($type['icon']) . '"></span> ' . $this->escapeHTML($subProps['displayPath']) . '</a></td>';
                $html .= '<td class="typeColumn">' . $this->escapeHTML($type['string']) . '</td>';
                $html .= '<td>';
                if (isset($subProps['{DAV:}getcontentlength'])) {
                    $html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes');
                }
                $html .= '</td><td>';
                if (isset($subProps['{DAV:}getlastmodified'])) {
                    $lastMod = $subProps['{DAV:}getlastmodified']->getTime();
                    $html .= $this->escapeHTML($lastMod->format('F j, Y, g:i a'));
                }
                $html .= '</td><td>';
                if (isset($subProps['{DAV:}displayname'])) {
                    $html .= $this->escapeHTML($subProps['{DAV:}displayname']);
                }
                $html .= '</td>';

                $buttonActions = '';
                if ($subProps['subNode'] instanceof DAV\IFile) {
                    $buttonActions = '<a href="' . $this->escapeHTML($subProps['fullPath']) . '?sabreAction=info"><span class="oi" data-glyph="info"></span></a>';
                }
                $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]);

                $html .= '<td>' . $buttonActions . '</td>';
                $html .= '</tr>';
            }

            $html .= '</table>';
        }

        $html .= '</section>';
        $html .= '<section><h1>属性</h1>';
        $html .= '<table class="propTable">';

        // Allprops request
        $propFind = new PropFindAll($path);
        $properties = $this->server->getPropertiesByNode($propFind, $node);

        $properties = $propFind->getResultForMultiStatus()[200];

        foreach ($properties as $propName => $propValue) {
            if (!in_array($propName, $this->uninterestingProperties)) {
                $html .= $this->drawPropertyRow($propName, $propValue);
            }
        }

        $html .= '</table>';
        $html .= '</section>';

        /* Start of generating actions */

        $output = '';
        if ($this->enablePost) {
            $this->server->emit('onHTMLActionsPanel', [$node, &$output, $path]);
        }

        if ($output) {
            $html .= '<section><h1>操作</h1>';
            $html .= "<div class=\"actions\">\n";
            $html .= $output;
            $html .= "</div>\n";
            $html .= "</section>\n";
        }

        $html .= $this->generateFooter();

        $this->server->httpResponse->setHeader('Content-Security-Policy', "default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';");

        return $html;
    }

    /**
     * This method is used to generate the 'actions panel' output for
     * collections.
     *
     * This specifically generates the interfaces for creating new files, and
     * creating new directories.
     *
     * @param mixed  $output
     * @param string $path
     */
    public function htmlActionsPanel(DAV\INode $node, &$output, $path)
    {
        if (!$node instanceof DAV\ICollection) {
            return;
        }

        // We also know fairly certain that if an object is a non-extended
        // SimpleCollection, we won't need to show the panel either.
        if ('Sabre\\DAV\\SimpleCollection' === get_class($node)) {
            return;
        }

        $output .= <<<HTML
<form method="post" action="">
<h3>新建目录</h3>
<input type="hidden" name="sabreAction" value="mkcol" />
<label>名称:</label> <input type="text" name="name" /><br />
<input type="submit" value="create" />
</form>
<form method="post" action="" enctype="multipart/form-data">
<h3>上传文件</h3>
<input type="hidden" name="sabreAction" value="put" />
<label>名称 (可选):</label> <input type="text" name="name" /><br />
<label>选择文件:</label> <input type="file" name="file" /><br />
<input type="submit" value="upload" />
</form>
HTML;
    }

    /**
     * Maps a resource type to a human-readable string and icon.
     *
     * @param DAV\INode $node
     *
     * @return array
     */
    private function mapResourceType(array $resourceTypes, $node)
    {
        if (!$resourceTypes) {
            if ($node instanceof DAV\IFile) {
                return [
                    'string' => '文件',
                    'icon' => 'file',
                ];
            } else {
                return [
                    'string' => 'Unknown',
                    'icon' => 'cog',
                ];
            }
        }

        $types = [
            '{http://calendarserver.org/ns/}calendar-proxy-write' => [
                'string' => 'Proxy-Write',
                'icon' => 'people',
            ],
            '{http://calendarserver.org/ns/}calendar-proxy-read' => [
                'string' => 'Proxy-Read',
                'icon' => 'people',
            ],
            '{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [
                'string' => 'Outbox',
                'icon' => 'inbox',
            ],
            '{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [
                'string' => 'Inbox',
                'icon' => 'inbox',
            ],
            '{urn:ietf:params:xml:ns:caldav}calendar' => [
                'string' => 'Calendar',
                'icon' => 'calendar',
            ],
            '{http://calendarserver.org/ns/}shared-owner' => [
                'string' => 'Shared',
                'icon' => 'calendar',
            ],
            '{http://calendarserver.org/ns/}subscribed' => [
                'string' => 'Subscription',
                'icon' => 'calendar',
            ],
            '{urn:ietf:params:xml:ns:carddav}directory' => [
                'string' => 'Directory',
                'icon' => 'globe',
            ],
            '{urn:ietf:params:xml:ns:carddav}addressbook' => [
                'string' => 'Address book',
                'icon' => 'book',
            ],
            '{DAV:}principal' => [
                'string' => 'Principal',
                'icon' => 'person',
            ],
            '{DAV:}collection' => [
                'string' => '目录',
                'icon' => 'folder',
            ],
        ];

        $info = [
            'string' => [],
            'icon' => 'cog',
        ];
        foreach ($resourceTypes as $k => $resourceType) {
            if (isset($types[$resourceType])) {
                $info['string'][] = $types[$resourceType]['string'];
            } else {
                $info['string'][] = $resourceType;
            }
        }
        foreach ($types as $key => $resourceInfo) {
            if (in_array($key, $resourceTypes)) {
                $info['icon'] = $resourceInfo['icon'];
                break;
            }
        }
        $info['string'] = implode(', ', $info['string']);

        return $info;
    }

    /**
     * Draws a table row for a property.
     *
     * @param string $name
     * @param mixed  $value
     *
     * @return string
     */
    private function drawPropertyRow($name, $value)
    {
        $html = new HtmlOutputHelper(
            $this->server->getBaseUri(),
            $this->server->xml->namespaceMap
        );

        return '<tr><th>' . $html->xmlName($name) . '</th><td>' . $this->drawPropertyValue($html, $value) . '</td></tr>';
    }

    /**
     * Draws a table row for a property.
     *
     * @param HtmlOutputHelper $html
     * @param mixed            $value
     *
     * @return string
     */
    private function drawPropertyValue($html, $value)
    {
        if (is_scalar($value)) {
            return $html->h($value);
        } elseif ($value instanceof HtmlOutput) {
            return $value->toHtml($html);
        } elseif ($value instanceof \Sabre\Xml\XmlSerializable) {
            // There's no default html output for this property, we're going
            // to output the actual xml serialization instead.
            $xml = $this->server->xml->write('{DAV:}root', $value, $this->server->getBaseUri());
            // removing first and last line, as they contain our root
            // element.
            $xml = explode("\n", $xml);
            $xml = array_slice($xml, 2, -2);

            return '<pre>' . $html->h(implode("\n", $xml)) . '</pre>';
        } else {
            return '<em>unknown</em>';
        }
    }
}
